package com.example.netcamerastreamdemo;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import com.google.gson.Gson;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

@RequiresApi(api = Build.VERSION_CODES.O)
public class TestHashTableActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    private static final String TAG = "TestHashTableActivity";

    WebSocket webSocket;
    Gson mGson = new Gson();
    static String dev_guid = "63-00F628C3B402-9A2A";
    static int video_chnn = 9;
    static int sub_chnn = 2;
    static String rec_path = "63-00F628C3B402-9A2A\\\\1623202389634\\\\63-00F628C3B402-9A2A_9_1623202389634";

    Hashtable<Integer, byte[]> mStreamData = new Hashtable<>();
    //    FramData mFramData;
    int mIndex = -1;
    byte[] firstBytes;
    SurfaceView surfaceView;
    WorkThread workThread = new WorkThread();
    boolean isFinished = false;
    File file;
    FileOutputStream fileOutputStream;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        surfaceView = findViewById(R.id.surface_view);
        surfaceView.getHolder().addCallback(this);
//        requestPermissions(new String[]{
//                Manifest.permission.READ_EXTERNAL_STORAGE,
//                Manifest.permission.WRITE_EXTERNAL_STORAGE
//        }, 0);
//        file = new File(getExternalFilesDir(null) + "/" + System.currentTimeMillis() + ".h264");
//        try{
//            fileOutputStream = new FileOutputStream(file);
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
    }

    public void initWebSocket(View view){
        OkHttpClient mClient = new OkHttpClient.Builder()
                .readTimeout(3, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(3, TimeUnit.SECONDS)//设置写的超时时间
                .connectTimeout(3, TimeUnit.SECONDS)//设置连接超时时间
                .build();

        String url = "wss://shike-qa.video110.cn/nsdk";
        Request request = new Request.Builder().get().url(url).build();
        webSocket = mClient.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                super.onOpen(webSocket, response);
                Log.i(TAG, "---onOpen===");
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                super.onMessage(webSocket, text);
                Log.i(TAG, "---onMessage===" + text);
            }

            @Override
            public void onMessage(WebSocket webSocket, ByteString bytes) {
                super.onMessage(webSocket, bytes);
                mIndex++;
                if (firstBytes == null){
                    firstBytes = bytes.substring(44).toByteArray();
                }
//                mFramData = new FramData(bytes.substring(44).toByteArray(), mIndex);
//                try {
//                    fileOutputStream.write(videoBytes);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
                mStreamData.put(mIndex, bytes.substring(44).toByteArray());
//                Log.i(TAG, "---onMessage===" + mIndex);
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                super.onClosing(webSocket, code, reason);
                Log.i(TAG, "---onClosing===" + reason);
            }

            @Override
            public void onClosed(WebSocket webSocket, int code, String reason) {
                super.onClosed(webSocket, code, reason);
                Log.i(TAG, "---onClosed===" + reason);
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                super.onFailure(webSocket, t, response);
                Log.i(TAG, "---onFailure===" + t.toString());
            }
        });
    }

    void videoIsPlaying(){

    }

    public void stopWebsocket(View view){
        if (webSocket == null){
            return;
        }
        webSocket.cancel();
    }

    public void sendStartVideoMsg(View view){
        MsgData startVideoMsg = new MsgData(
                "VSDK_CMD_TX_RT_VIDEO_START",
                dev_guid,
                video_chnn,
                sub_chnn,
                rec_path);
        webSocket.send(mGson.toJson(startVideoMsg));
        videoIsPlaying();
    }

    public void sendStopVideoMsg(View view){
        MsgData stopVideoMsg = new MsgData(
                "VSDK_CMD_TX_RT_VIDEO_STOP",
                dev_guid,
                video_chnn,
                sub_chnn,
                rec_path);
        webSocket.send(mGson.toJson(stopVideoMsg));
        isFinished = true;
        firstBytes = null;
//        try {
//            fileOutputStream.close();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
        Log.i(TAG, "---surfaceCreated===");
        workThread.start();
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        Log.i(TAG, "---surfaceChanged===");

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
        Log.i(TAG, "---surfaceDestroyed===");
    }

    private class WorkThread extends Thread{
        private MediaCodec.BufferInfo mBufferInfo;
        private MediaCodec decoder;
        int mWidth = 704;
        int mHeight = 576;
        //        int mWidth = 640;
//        int mHeight = 480;
        Long TIMEOUT_US = 400 * 1000L ;
        int decodeCount = 0;

        @Override
        public void run() {
            while (!isFinished) {
                if (null == mStreamData.get(0)){
//                    Log.i(TAG, "---mFramData == null===");
                    continue;
                }
                if (null == mStreamData.get(1)) {
                    prepare(mStreamData.get(0));
                }
                decode();
            }
        }

        private void prepare(byte[] bytes){
            Log.i(TAG, "---prepare===");
            int pos=0;
            byte[] spspps = bytes;

            if(!((pos+3<spspps.length)&&(spspps[pos]==0&&spspps[pos+1]==0&&spspps[pos+2]==0&&spspps[pos+3]==1))){
//                return false;
            }else {
                //00 00 00 01开始标志后的一位
                pos=4;
            }
            while((pos+3<spspps.length)&&!(spspps[pos]==0&&spspps[pos+1]==0&&spspps[pos+2]==0&&spspps[pos+3]==1)){
                pos++;
            }
            byte[] mSps = Arrays.copyOfRange(spspps, 0, pos);
            byte[] mPps = Arrays.copyOfRange(spspps, pos, spspps.length);
            mBufferInfo = new MediaCodec.BufferInfo();
            MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
//            format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 9999999);
//            format.setInteger(MediaFormat.KEY_MAX_HEIGHT, mHeight);
//            format.setInteger(MediaFormat.KEY_MAX_WIDTH, mWidth);
//            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
//            format.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
//            format.setInteger(MediaFormat.KEY_BIT_RATE, mWidth * mHeight);
//            format.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ);

            format.setByteBuffer("csd-0", ByteBuffer.wrap(mSps));
            format.setByteBuffer("csd-1", ByteBuffer.wrap(mPps));
            try {
                decoder = MediaCodec.createDecoderByType("video/avc");
            } catch (IOException e) {
                e.printStackTrace();
            }
            decoder.configure(format, surfaceView.getHolder().getSurface(), null, 0);
            decoder.start();
        }

        private void decode(){
            Log.i(TAG, "---decode===");
            boolean isEnd = false;
            while (!isEnd){
                int inIndex = decoder.dequeueInputBuffer(TIMEOUT_US);
                Log.i(TAG, "---inIndex===" + inIndex);
                if (inIndex >= 0){
                    ByteBuffer buffer = decoder.getInputBuffer(inIndex);
//                    if (buffer == null)return;
                    buffer.clear();
                    byte[] bytes = mStreamData.get(decodeCount);
                    Log.i(TAG, "---put.data===" + decodeCount);

                    decodeCount++;

                    if (bytes == null) {
                        Log.i(TAG, "---mFramData.data == null===");
                        decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    }else {
                        buffer.put(bytes, 0, bytes.length);
                        decoder.queueInputBuffer(inIndex, 0, bytes.length, 0, MediaCodec.BUFFER_FLAG_KEY_FRAME);
                    }
                }else {
                    isEnd = true;
                }
                int outIndex = decoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
                while (outIndex >= 0){
                    decoder.releaseOutputBuffer(outIndex, true);
                    outIndex = decoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
                }
            }
        }
    }

    public static class FrameData {
        byte[] data;
        long time;

        public FrameData(byte[] data) {
            this.data = data;
            this.time = System.nanoTime();
        }
    }

    public static class MsgData{
        public MsgData(String cmd, String dev_guid, int video_chnn, int sub_chnn, String rec_path) {
            this.cmd = cmd;
            this.dev_guid = dev_guid;
            this.video_chnn = video_chnn;
            this.sub_chnn = sub_chnn;
            this.rec_path = rec_path;
        }

        String cmd;
        String dev_guid;
        int video_chnn;
        int sub_chnn;
        String rec_path;
    }
}